﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using SkynetMongoDBAccess.Converter;
using MongoDB;
using MongoDB.GridFS;

namespace SkynetMongoDBAccess
{
    /// <summary>
    /// mongodb数据库操作基类
    /// </summary>
    ///<remarks>
    /// 作者：         李志强
    /// 开发时间：  2011-04-18
    /// </remarks>
    public abstract class MongoAbstractDB : IDisposable
    {
        //链接字符串
        private string _connectionString = "mongodb://localhost";
        //数据库名
        private string _databaseName = "myDatabase";
        //读写文件缓存
        private long _fileRWLength = 2048;    //
       
        private Mongo _mongo;
        private MongoDatabase _mongoDatabase;

        //"Server=localhost,27017;database=myDatabase;username=root;password=root;ConnectTimeout=300000;ConnectionLifetime=300000;MinimumPoolSize=25;MaximumPoolSize=25;Pooled=true"
        public MongoAbstractDB(string connectionString)
        {
            this._connectionString = connectionString;
            string[] connections = connectionString.Split(';');
            this._databaseName = connections.Where(o => o.ToLower().StartsWith("database")).First().Substring(9);
            this.ServerName = connections.Where(o => o.ToLower().StartsWith("server")).First().Substring(7);
         
        }

        public MongoAbstractDB(string connection,long fileCacheMaxLength):this(connection)
        {
            if (fileCacheMaxLength > this._fileRWLength)
            {
                this._fileRWLength = fileCacheMaxLength;
            }
        }
 
        public string ServerName { get; private set; }
        public string DBName { get { return this._databaseName; } }
        public string CollectionName { get;protected set; }
        public long RWFileCacheMaxLength { get { return _fileRWLength; } }
        /// <summary>
        /// 连接数据库
        /// 注意：_mongo.TryConnect()也是进行数据库连接
        /// 当连接成功后，会放入缓冲池中，默认是100.当缓冲池超出会抛出异常
        /// 当关闭连接时，并不销毁缓冲池的数据，只是放置到另一个缓冲池中以供下一次使用
        /// </summary>
        public void Open()
        {
            if (_mongo == null)
            {
                _mongo = GetMongo(_connectionString);
              
            }
            if (!_mongo.TryConnect())
            {
                throw new Exception("数据库连接异常");
            }
            //查询出所有用户 测试 目的 为验证用户名和密码
            GetUser(DBName);
        }

        protected void Open(string dbname)
        {
            if (string.IsNullOrEmpty(dbname))
            {
                dbname = _databaseName;
            }
            if (_mongo == null)
            {
                this._databaseName = dbname;
                _mongo = GetMongo(_connectionString);
            }
            if (this.DBName != dbname)
            {
                _mongoDatabase = this._mongo.GetDatabase(dbname) as MongoDatabase;
            }
        }

        protected virtual Mongo GetMongo(string connection)
        {
            if (_mongo == null)
            {
                _mongo = new Mongo(_connectionString);
            }
            return _mongo;
        }
        public virtual MongoDatabase GetDataBase
        {
            get
            {
                if (_mongoDatabase == null)
                {
                    _mongoDatabase = this._mongo.GetDatabase(_databaseName) as MongoDatabase;
                }
                return _mongoDatabase;
            }
        }

        /// <summary>
        /// 获取数据库中所有用户
        /// </summary>
        /// <param name="dbname">数据库名称</param>
        /// <returns></returns>
        public List<UserEntity> GetUser(string dbname)
        {
            if (string.IsNullOrEmpty(dbname))
            {
                dbname = _databaseName;
            }
            List<UserEntity> userList = new List<UserEntity>();
            IEnumerable<Document> users = _mongo.GetDatabase(dbname).Metadata.ListUsers().Documents;
            foreach (Document user in users)
            {
                userList.Add(user.ConvertToUserEntity());
            }
            return userList;
        }
        /// <summary>
        /// 添加用户
        /// </summary>
        /// <param name="dbname">数据库名称</param>
        /// <param name="username">用户名</param>
        /// <param name="pwd">密码</param>
        public void AddUser(string dbname,string username,string pwd)
        {
            if (string.IsNullOrEmpty(dbname))
            {
                dbname = _databaseName;
            }
            _mongo.GetDatabase(dbname).Metadata.AddUser(username, pwd);
        }
        /// <summary>
        /// 删除用户
        /// </summary>
        /// <param name="dbname">数据库名称</param>
        /// <param name="username">用户名</param>
        public void RemoveUser(string dbname, string username)
        {
            if (string.IsNullOrEmpty(dbname))
            {
                dbname = _databaseName;
            }
            _mongo.GetDatabase(dbname).Metadata.RemoveUser(username);
        }
        /// <summary>
        /// 获取所有的数据库名称
        /// </summary>
        /// <returns></returns>
        public List<string> GetDataBases()
        {
            List<string> list = new List<string>();
            foreach (IMongoDatabase db in _mongo.GetDatabases())
            {
                list.Add(db.Name);
            }
            return list;
        }
        /// <summary>
        /// 获取指定数据库的所以集合名称
        /// </summary>
        /// <param name="databaseName">数据库名称</param>
        /// <returns></returns>
        public List<string> GetCollecton(string databaseName)
        {
            if (string.IsNullOrEmpty(databaseName))
            {
                databaseName = _databaseName;
            }
            List<string> list = _mongo.GetDatabase(databaseName).GetCollectionNames();
            try
            {
                string[] systemdata = new string[]{"fs","system"};
                return list.Select(o => o.Split('.')[1]).Where(
                    o=>
                        !systemdata.Contains(o)
                        ).Distinct().ToList();
            }
            catch { }
            return list;
        }
        /// <summary>
        /// 删除指定的数据库
        /// </summary>
        /// <param name="databaseName">数据库名称</param>
        /// <returns></returns>
        public bool DropDataBase(string databaseName)
        {
            if (string.IsNullOrEmpty(databaseName))
            {
                databaseName = _databaseName;
            }
            //删除数据库时。创建的索引必须要手动删除
            return _mongo.GetDatabase(databaseName).Metadata.DropDatabase();
        }
        /// <summary>
        /// 删除指定的集合
        /// </summary>
        /// <param name="databaseName">数据库名称</param>
        /// <param name="collectionName">集合名称</param>
        /// <returns></returns>
        public bool DropCollection(string databaseName, string collectionName)
        {
            if (string.IsNullOrEmpty(databaseName))
            {
                databaseName = _databaseName;
            }
            //删除索引
            foreach (string  indexname in GetIndexs(databaseName, collectionName))
            {
                DropIndex(databaseName, collectionName, indexname);
            }
            return _mongo.GetDatabase(databaseName).Metadata.DropCollection(collectionName);
        }
       

        #region Index
        /// <summary>
        /// 获取所有索引
        /// </summary>
        /// <returns></returns>
        public List<string> GetIndexs(string databaseName, string collectionName)
        {
            List<string> list = new List<string>();
            foreach (string index in _mongo.GetDatabase(databaseName).GetCollection(collectionName).Metadata.Indexes.Keys)
            {
                list.Add(index);
            }
            return list;
        }
        /// <summary>
        /// 创建索引
        /// </summary>
        /// <param name="document">索引条件  ｛indexname：1｝ 1为升序 -1 为降序</param>
        /// <param name="isOnly">true 为唯一索引</param>
        public void CreateIndex(IDictionary<string, object> filter, bool isOnly)
        {
            Document document = new Document(filter);
            _mongo.GetDatabase(DBName).GetCollection(CollectionName).Metadata.CreateIndex(document, isOnly);
        }
        /// <summary>
        /// 删除索引
        /// </summary>
        /// <param name="indexName">_name_</param>
        public void DropIndex(string indexName)
        {
            if (indexName != "_id_")
            {
                _mongo.GetDatabase(DBName).GetCollection(CollectionName).Metadata.DropIndex(indexName);
            }
        }
        private void DropIndex(string databaseName, string collectionName,string indexName)
        {
            if (indexName != "_id_")
            {
                _mongo.GetDatabase(databaseName).GetCollection(collectionName).Metadata.DropIndex(indexName);
            }
        }
        #endregion

        #region GridFS
        public bool GridFsExists(string fileName)
        {
            GridFile gridFile = new GridFile(GetDataBase);
            return gridFile.Exists(fileName);
        }
        /// <summary>
        /// 保存文件(如作为服务需要第3个参数)
        /// </summary>
        /// <param name="fileName">文件名称</param>
        /// <param name="byteFile">文件数据</param>
        /// <param name="isAppend">是否是续传</param>
        public string  GridFsSaveService(string fileName, byte[] byteFile,bool isAppend)
        {
            GridFile gridFile = new GridFile(GetDataBase);
            FileMode mode = FileMode.Create;
            if (isAppend)
            {
                mode = FileMode.Append;
            }
            using (GridFileStream stream = gridFile.Create(fileName,mode, FileAccess.Write))
            {
                stream.Write(byteFile, 0, byteFile.Length);
            }
            return fileName;
        }
        public string GridFsSave(string fileName, byte[] byteFile)
        {
            return GridFsSaveService(fileName, byteFile, false);
        }
        /// <summary>
        /// 保存文件
        /// </summary>
        /// <param name="filePath">文件路径</param>
        /// <param name="progressAction">进度委托</param>
        /// <returns></returns>
        public string GridFsSave(string filePath, Action<long, long> progressAction, bool isRename)
        {
            if (!File.Exists(filePath))
            {
                throw new Exception("文件不存在");
            }
            GridFile gridFile = new GridFile(GetDataBase);

            string fileName = Path.GetFileName(filePath);
            if (isRename)
            {
                string[] filesplit =  fileName.Split('.');
                fileName = filesplit[0].ToString() + DateTime.Now.ToString("yyyyMMddHHmmss")  + "."+ filesplit[1].ToString();
            }


            using (GridFileStream stream = gridFile.Create(fileName))
            {
                stream.GridFileInfo.ContentType = GetTypeContent(fileName);
                byte[] data = new byte[_fileRWLength];
               for(int i=0;i<5;i++)
                {
                    try
                    {
                        using (FileStream filestream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read))
                        {
                            long fileReadCount = 0L;
                            long fileCount = filestream.Length;
                            while (true)
                            {
                                int count = filestream.Read(data, 0, data.Length);
                                if (count <= 0)
                                    break;
                                stream.Write(data, 0, count);
                                stream.Flush();
                                fileReadCount += count;
                                //报告进度
                                if (progressAction != null)
                                {
                                    progressAction(fileReadCount, fileCount);
                                }
                            }
                            if (fileReadCount != fileCount)
                            {
                                throw new Exception("文件写入错误，长度不匹配");
                            }
                        }
                        break;
                    }
                    catch
                    {
                        System.Threading.Thread.Sleep(1000);
                    }
                }
            }
            return fileName;
        }
        /// <summary>
        /// 异步保存文件  
        /// </summary>
        /// <param name="fileName">文件名称</param>
        /// <param name="byteFile">文件数据</param>
        /// <param name="callback">保存成功后委托</param>
        public void  BeginGridFsSave(string fileName, byte[] byteFile,Action<string> callback)
        {
            var saveAction = new Func<string, byte[], string>(GridFsSave);
            saveAction.BeginInvoke(fileName, byteFile, new AsyncCallback(async => 
            {
                Func<string, byte[], string> action = async.AsyncState as Func<string, byte[], string>;
                string filename = action.EndInvoke(async);
                if (callback != null)
                {
                    callback(filename);
                }
            }), saveAction);
        }
        public void BeginGridFsSave(string filePath, Action<long, long> progressAction,bool isRename, Action<string> callback)
        {
            var saveAction = new Func<string, Action<long, long>,bool, string>(GridFsSave);
            saveAction.BeginInvoke(filePath, progressAction, isRename,new AsyncCallback(async =>
            {
                Func<string, Action<long, long>, string> action = async.AsyncState as Func<string, Action<long, long>, string>;
                string filename = action.EndInvoke(async);
                if (callback != null)
                {
                    callback(filename);
                }
            }), saveAction);
        }

        /// <summary>
        /// 根据文件路径保存文件
        /// </summary>
        /// <param name="filePath"></param>
        /// <returns></returns>
        //public string GridFsSave(string filePath)
        //{
        //    string filename = Path.GetFileName(filePath);
        //    try
        //    {
        //        List<byte> bytelist = new List<byte>();
        //        using (FileStream stream = new FileStream(filePath, FileMode.Open))
        //        {
        //            byte[] data = new byte[stream.Length];
        //            stream.Read(data, 0, data.Length);
        //            bytelist.AddRange(data);
        //        }
        //        //当文件大于20M 采用多线程操作
        //        if (bytelist.Count > 20480)
        //        {
        //            Dictionary<string, byte[]> dic = new Dictionary<string, byte[]>();
        //            dic.Add(filename, bytelist.ToArray());
        //            System.Threading.ThreadPool.QueueUserWorkItem(new System.Threading.WaitCallback((object obj) =>
        //            {
        //                Dictionary<string, byte[]> paramDic = obj as Dictionary<string, byte[]>;
        //                string file = paramDic.Keys.First();
        //                GridFsSave(file, paramDic[file]);
        //            }), dic);
        //        }
        //        else
        //        {
        //            GridFsSave(filename, bytelist.ToArray());
        //        }
        //    }
        //    catch
        //    {
        //        return string.Empty;
        //    }
        //    return filename;
        //}

        /// <summary>
        /// 以服务的形式进行读取数据
        /// </summary>
        /// <param name="fileName">文件名称</param>
        /// <param name="index">当前读取位置</param>
        /// <param name="count">要返回的字节数</param>
        /// <param name="fileLength">文件的总大小</param>
        /// <returns>字节</returns>
        public byte[] GridFsReadService(string fileName,int index, int count,out long fileLength)
        {
            fileLength = 0;
            GridFile gridFile = new GridFile(GetDataBase);
            if (gridFile.Exists(fileName))
            {
                using (GridFileStream stream = gridFile.OpenRead(fileName))
                {
                    fileLength = stream.Length;
                    byte[] bytes = new byte[fileLength];
                    stream.Read(bytes,index, count);
                    return bytes;
                }
            }
            return null;
        }
        /// <summary>
        /// 读取指定的文件
        /// </summary>
        /// <param name="fileName">文件名称</param>
        /// <returns></returns>
        public byte[] GridFsRead(string fileName)
        {
            GridFile gridFile = new GridFile(GetDataBase);
            if (gridFile.Exists(fileName))
            {
                using (GridFileStream stream = gridFile.OpenRead(fileName))
                {
                    byte[] bytes = new byte[stream.Length];
                    stream.Read(bytes, 0, bytes.Length);
                    return bytes;
                }
            }
            return null;
        }
        /// <summary>
        /// 读取文件
        /// </summary>
        /// <param name="fileName">文件名称</param>
        /// <param name="progressAction">进度委托</param>
        /// <returns></returns>
        public byte[] GridFsRead(string fileName, Action<long, long> progressAction)
        {
            try
            {
                GridFile gridFile = new GridFile(GetDataBase);
                if (gridFile.Exists(fileName))
                {
                    using (GridFileStream stream = gridFile.OpenRead(fileName))
                    {
                        long fileCount = stream.Length;
                        byte[] filedata = new byte[fileCount];
                        if (_fileRWLength > fileCount)
                        {
                            stream.Read(filedata, 0, filedata.Length);
                        }
                        else
                        {
                            long fileReadCount = 0L;
                            byte[] _fileCache = new byte[_fileRWLength];
                            while (true)
                            {
                                int count = stream.Read(_fileCache, 0, _fileCache.Length);
                                if (count <= 0)
                                    break;
                                Array.Copy(_fileCache, 0, filedata, fileReadCount, count);
                                fileReadCount += count;
                                if (progressAction != null)
                                {
                                    progressAction(fileReadCount, fileCount);
                                }
                            }
                        }
                        return filedata;
                    }
                }
            }
            catch { }
            return null; 
            //throw new Exception("此文件不存在");
        }
        public void BeginGridFsRead(string fileName, Action<long, long> progressAction,Action<byte[]> callback)
        {
            if (callback == null) return;
            var actionread = new Func<string, Action<long, long>, byte[]>(GridFsRead);
            actionread.BeginInvoke(fileName, progressAction, new AsyncCallback(asyn => {
                Func<string, Action<long, long>, byte[]> delgate = asyn.AsyncState as Func<string, Action<long, long>, byte[]>;
                byte[] data = delgate.EndInvoke(asyn);
                callback(data);
            }),actionread);
        }
        public void BeginGridFsRead(string fileName, Action<byte[]> callback)
        {
            if (callback == null)
                return;
            var actionRead = new Func<string, byte[]>(GridFsRead);
            actionRead.BeginInvoke(fileName, new AsyncCallback(asyn => 
            {
               Func<string,byte[]> delgate =  asyn.AsyncState as Func<string,byte[]>;
              byte[] data =  delgate.EndInvoke(asyn);
              callback(data);
            }), actionRead);
        }
        /// <summary>
        /// 删除指定的文件(包含删除)
        /// </summary>
        /// <param name="fileName"></param>
        public void GridFsContainsDelete(string columnName, string valueName)
        {
            GridFile gridFile = new GridFile(GetDataBase);
            gridFile.Delete(new Document(columnName, new MongoRegex(string.Format("{0}", valueName))));
        }
        public void GridFsDelete(string columnName, string valueName)
        {
            GridFile gridFile = new GridFile(GetDataBase);
            gridFile.Delete(new Document("$where",string.Format("this.{0} == \"{1}\"", columnName, valueName)));
        }
        /// <summary>
        /// 删除所有的文件
        /// </summary>
        public void GridFsDeleteAll()
        {
            GridFile gridFile = new GridFile(GetDataBase);
            IEnumerable<Document> files = gridFile.ListFiles().Documents;
            foreach (Document doc in files)
            {
                GridFsDelete("filename",doc["filename"].ToString());
            }
        }
        /// <summary>
        /// 获取所有的文件信息
        /// </summary>
        /// <returns></returns>
        public List<GridFileEntity> GridFsAll()
        {
            GridFile gridFile = new GridFile(GetDataBase);
            IEnumerable<Document> files = gridFile.ListFiles().Documents;
            List<GridFileEntity> list = new List<GridFileEntity>();
            foreach (Document f in files)
            {
                list.Add(f.ConvertToGridFileEntity());
            }
            return list;
        }
        /// <summary>
        /// 分页获取所有文件信息
        /// </summary>
        /// <param name="index">从第几条记录开始</param>
        /// <param name="count">返回的记录数</param>
        /// <returns></returns>
        public List<GridFileEntity> GridFsAll(int index, int count,out int maxCount)
        {
            maxCount = 0;
            GridFile gridFile = new GridFile(GetDataBase);
            var filedata = gridFile.ListFiles();
            maxCount = filedata.Documents.Count();
            List<Document> files = filedata.Documents.Skip(index).Take(count).ToList();
            List<GridFileEntity> list = new List<GridFileEntity>();
            foreach (Document f in files)
            {
                list.Add(f.ConvertToGridFileEntity());
            }
            return list;
        }
        public List<GridFileEntity> GridFsAll(IDictionary<string, object> filter)
        {
            GridFile gridFile = new GridFile(GetDataBase);
            Document document = new Document(filter);
            var filedata = gridFile.ListFiles(document);
            List<Document> files = filedata.Documents.ToList();
            List<GridFileEntity> list = new List<GridFileEntity>();
            foreach (Document f in files)
            {
                list.Add(f.ConvertToGridFileEntity());
            }
            return list;
        }
        public List<GridFileEntity> GridFsAll(IDictionary<string, object> filter, int index, int count, out int maxCount)
        {
            GridFile gridFile = new GridFile(GetDataBase);
            Document document = new Document(filter);
            maxCount = gridFile.ListFiles(document).Documents.Count();
            List<Document> files = gridFile.ListFiles(document).Skip(index).Limit(count).Documents.ToList();

            List<GridFileEntity> list = new List<GridFileEntity>();
            foreach (Document f in files)
            {
                list.Add(f.ConvertToGridFileEntity());
            }
            return list;
        }
        #endregion

       

        /// <summary>
        /// 获取文件的类型名称（Content Type）
        /// </summary>
        /// <param name="fileName"></param>
        private string GetTypeContent(string fileName)
        {
            string contentType = "application/octetstream";

            string ext = Path.GetExtension(fileName);

            Microsoft.Win32.RegistryKey registryKey = Microsoft.Win32.Registry.ClassesRoot.OpenSubKey(ext);
            if (registryKey != null && registryKey.GetValue("Content Type") != null)
                contentType = registryKey.GetValue("Content Type").ToString();
            return contentType;
        }

        #region IDisposable 成员

        public void Dispose()
        {
            if (_mongo != null)
            {
                 _mongo.Disconnect();
                this._mongo.Dispose();
                this._mongo = null;
            }
            this._mongoDatabase = null;
        }

        #endregion
    }

}
